'''



name_aes：企业名加解密
contact_aes：联系方式，人名都是用这个加解密

contact_col.index_information() 根据索引去查数据

source ~/moonbrook/local/bin/activate
cd ~/moonbrook/Moonbrook/dsf_scripts/

name_aes：企业名加解密
contact_aes：联系方式，人名都是用这个加解密
decrypt_and_write : 全dict解密


需求：由于导数据、统计数据的需求非常频繁，会占用爬虫工程师很多时间去做重复性工作（统计导出），
严重影响爬虫工程师的工作效率，因此我打算将导数据与统计需求抽离出来，让开发只关注具体逻辑代码，
减少日志、进度、内存等信息的干扰。
由于统计导出需求过于简单，拟打算直接使用简化的单表 sql进行抽离，如有涉及多表统计导出，需要与开发沟通

        *，xx字段名             between 2020 and 2021  int [x,y]          聚合
select count(需要的字段) from  字段条件  <and or>  字段条件            groupby 字段名
        count_dup 去重

                                                                                   限制导出数量       按某字段排序
select dc(需要的字段) from  字段条件  <and or>  字段条件            groupby 字段名            limit 1000    orderby xx字段  AEC|desc


以下为三个比较常见的需求：
1、统计库内某表数据量   select count(*) from xx表  统计_all
2、统计某表各个源数据量  select * from xx表  groupby  源字段  统计_all
3、统计某表 某时间段的数据量   select * from  xx表  where  时间字段 between 2020 and 2021 【in [x,y]】 统计_all


1、导出某表各个源的xx条数据进行抽烟    select *  from xx表  groupby  源字段  导出_limit_1000条
2、导出某表某源某时间段的数据

3、导出某表各个数据源的xx条数据进行抽验
4、

数据摸底需求：
随机导出某表xxx条数据

一般格式 ： 某表
select from xx表 where  xx字段 条件  and(or) xx字段 条件  【导出xx条、随机取xx条 统计数量】 【groupby source  limit xx条】


未能解决的需求：
统计各个源的数据量，并各自随机导出xxx条

目前主要统计服方向：
1、统计数据
2、导出数据
3、发送钉钉
4、漏斗监控

导数及统计需求抽离
1、加解密
2、日志文件
3、时间管理
4、内存监控
5、固定流程


db.数据库名.find(字段条件).limit(导出数量 )
db.数据库名.find(字段条件).count()  #统计
'''

# !/usr/bin/env python
# -*- coding: utf-8 -*-

import json
import sys
import re
import psutil
import time
import hashlib
from binascii import a2b_hex
from base64 import b64encode
import requests
from copy import deepcopy
from datetime import datetime, timedelta
from hashlib import md5
from pybloom_live import ScalableBloomFilter
from secret import batch_encrypt_decrypt_dict
from aes import contact_aes, name_aes
from apscheduler.schedulers.blocking import BlockingScheduler
from datastore.mongodb import MongoDBClientWrapper

sys.path.append('/mnt/moonbrook/Moonbrook')
reload(sys)
sys.setdefaultencoding('utf-8')
db = MongoDBClientWrapper()
contact_col = db.p_col('contact')
enterprise_col = db.p_col('enterprise')

from pymongo import MongoClient
from configs import config_obj

silk_config = config_obj['silk_mongodb']
silk_client = MongoClient(silk_config['host'], connect=False)
silk_db = silk_client[silk_config['database']]
silk_db.authenticate(silk_config['user'], silk_config['pwd'])

silk_icp=silk_db.get_collection('icp')


class Utils(object):

    @staticmethod
    def get_date_range(ndays=0, end_time=None):
        '''
        得到一个时间范围
        @param ndays: int 时间间隔
        @param end_time: 结束时间 datetime or format str,默认是今天
        @return: tuple of datetime
        get_date_range(ndays=7,end_time="2021-9-30") ==>
        (datetime.datetime(2021, 9, 23, 0, 0), datetime.datetime(2021, 9, 30, 0, 0))
        '''
        n_day = timedelta(days=ndays)
        if end_time:
            if isinstance(end_time, datetime):
                start_time = end_time - n_day
            else:
                end_time = datetime.strptime(end_time, "%Y-%m-%d")
                start_time = end_time - n_day
        else:
            end_time = datetime.now()
            # today = datetime(year=2021, month=6, day=4)
            start_time = end_time - n_day
        _min = datetime(year=start_time.year, month=start_time.month, day=start_time.day)
        _max = datetime(year=end_time.year, month=end_time.month, day=end_time.day)
        return _min, _max

    @staticmethod
    def page_url_to_business_id(target_str):
        '''
        target_str -> 16位md5 -> 16进行编码 -> 12位base64编码
        @param target_str:
        @return:
        '''
        md5 = hashlib.md5()
        md5.update(target_str.encode(encoding='utf-8'))
        md5_text = md5.hexdigest()[8:24]
        hex_bytes = a2b_hex(md5_text)  # b'\xd5\xd0\xb3gc0\xd6\xa1'
        business_id = b64encode(hex_bytes).decode()
        return business_id

    @staticmethod
    def strf_datatime(result_dic):
        '''
        递归格式化dict中的所有datetime
        @param result_dic:dict
        @return:dict
        '''
        for k, v in result_dic.items():
            if isinstance(v, datetime):
                result_dic[k] = result_dic[k].strftime("%Y-%m-%d %H:%M:%S")
            if isinstance(v, dict):
                Utils.strf_datatime(v)
            if isinstance(v, list):
                for index, value in enumerate(v):
                    if isinstance(value, dict):
                        v[index] = Utils.strf_datatime(value)
        return result_dic

    @staticmethod
    def decrypt_one_record(record_dic, need_strf_time=False, need_decrypt=False):
        '''
        有些需求需要存json，所以要strftime，有些要原生datetime，有些要解密的数据，有些不要
        @param record_dic: dict
        @param need_strf_time: bool,是否需要格式化datetime对象
        @param need_decrypt: bool,是否需要解密整条数据
        @return: dict
        '''
        record_dic = deepcopy(record_dic)
        is_change_flag = False
        if need_strf_time:
            record_dic = Utils.strf_datatime(record_dic)
        if need_decrypt:
            is_change_flag = True
            try:
                record_dic = batch_encrypt_decrypt_dict([record_dic], 'decrypt')[0]
            except:
                print('batch_encrypt_decrypt_dict  decrypt error')
        if not is_change_flag:
            # 如果既没need_strf_time又没传入need_decrypt，则抛出异常
            raise Exception('dong need decrypt_one_record')
        return record_dic

    @staticmethod
    def get_nameIds_by_ent_name(enterprise_name):
        '''
        通过企业名获取nameIds
        @param enterprise_name: 企业名 str
        @return: nameId_ls
        '''
        name_md5 = md5(name_aes.encrypt(enterprise_name)).hexdigest()[8:-8]
        cursor = enterprise_col.find({"nameMd5": name_md5})
        print('{}有{}个nameId'.format(enterprise_name, cursor.count()))
        nameId_ls = []
        for ent_record_dic in cursor:
            nameId = ent_record_dic.get('_id')
            nameId_ls.append(nameId)
        return nameId_ls

    def get_nameIds_by_ent_name_ls(self, enterprise_name_ls):
        '''
        批量将企业名转为nameId
        @param enterprise_name_ls:
        @return:
        '''
        nameId_ls = []
        for enterprise_name in enterprise_name_ls:
            nameIds = self.get_nameIds_by_ent_name(enterprise_name)
            if nameIds:
                nameId_ls.append(nameIds[0])
            else:
                nameId_ls.append(None)

    def get_ent_name_by_nameId(self, nameId):
        '''
        通过nameId得到企业名称
        @param nameId:
        @return:ent_name
        '''
        result_dic = enterprise_col.find_one({"_id": nameId})
        return result_dic.get('name')

    def get_ent_name_ls_by_nameId_ls(self, nameId_ls):
        '''
        通过nameId列表得到企业名称列表
        @param nameId:
        @return:ent_name
        '''
        ent_name_ls = []
        for nameId in nameId_ls:
            result_dic = enterprise_col.find_one({"_id": nameId})
            ent_name_ls.append(result_dic.get('name'))
        return ent_name_ls


    def get_contact_by_ent_name(self, enterprise_name):
        '''
        通过企业名获取所有联系方式
        @param enterprise_name: 企业名称
        @return:
        '''
        for nameId in self.get_nameIds_by_ent_name(enterprise_name):
            print('{}\tnameId：{}'.format(enterprise_name, nameId))
            contact_cursor = contact_col.find({'nameId': nameId})
            for contact_record_dic in contact_cursor:
                contact_record_dic = self.decrypt_one_record(contact_record_dic, need_decrypt=True)
                _contact = contact_record_dic.get('contact')
                _contact_type = contact_record_dic.get('contactType')
                source = '----'.join(
                    [dic.get('contactSource', "") for dic in contact_record_dic['contactChannelList']])
                if _contact:
                    print('{}\t{}\t{}'.format(_contact_type, _contact, source))
            else:
                print('---- end ----')


    def get_contact_by_ent_name_ls(self, enterprise_name_ls):
        '''
        通过企业名获取 所有联系方式及来源
        @param enterprise_name_ls: 企业名列表 list of enterprise_name
        @return:None
        '''
        for enterprise_name in enterprise_name_ls:
            self.get_contact_by_ent_name(enterprise_name)

    def get_decrypt_contact_by_name_id(self, nameId):
        '''
        通过nameId找到该企业所有联系方式数据
        @param nameId:
        @return:
        '''
        # projection = ['import_update_time','contact','create_time','contactType','contactChannelList','name','contactSource','contactPageLink']
        dic_ls = list(contact_col.find({'nameId': nameId}))
        new_dic_ls = []
        for encrypt_dic in dic_ls:
            decrypt_dic = self.decrypt_one_record(encrypt_dic, need_decrypt=True)
            new_dic_ls.append(decrypt_dic)
        print('{} 共有 {}条 联系方式'.format(nameId, len(dic_ls)))
        return new_dic_ls

    def get_one_record_by_nameId_and_contact(self, nameId, contact):
        '''
        通过nameId找到该企业所有联系方式数据,然后返回 指定的contact那条数据
        @param nameId:
        @param contact:
        @return:
        '''
        dic_ls = self.get_decrypt_contact_by_name_id(nameId)
        for dic in dic_ls:
            if dic.get('contact') == contact:
                return dic
        else:
            print('库内 {} 不存在该联系方式：{}'.format(nameId, contact))

    def get_one_record_by_name_and_contact(self,name,contact):
        nameIds = self.get_nameIds_by_ent_name(name)
        records = []
        for name_id in nameIds:
            dic = self.get_one_record_by_nameId_and_contact(name_id,contact)
            print("===========nameId  {}=========".format(name_id))
            records.append(dic)
        return records

    @staticmethod
    def bloom_filter_fac(initial_capacity=1000000, error_rate=0.001, mode=2):
        '''
        bloom工厂方法配置
        @param initial_capacity: int
        @param error_rate: float
        @param mode: int  2慢但是节省内存、4快耗内存
        @return: bloom
        '''
        return ScalableBloomFilter(initial_capacity=initial_capacity, error_rate=error_rate, mode=mode)  # 慢点但是省内存。

utils = Utils()


# if __name__ == '__main__':
#     enterprise_name_ls = [
#         '河北振灏环保设备有限公司',
#         '江苏和爱建设工程有限公司',
#         '驰飞(重庆)通用航空有限公司'
#     ]
#     utils.get_contact_by_name_ls(enterprise_name_ls)

'''
# 常见需求：
1、通过一个企业名查找其所有联系方式数据
2、判断一个联系方式是否存在库内
3、通过企业名和联系方式输出该条联系方式
'''
#
# tmux new -s dsf
# cd /mnt/moonbrook/Moonbrook/dsf_scripts
# source /mnt/moonbrook/local/bin/activate
